/*
 * XTaskManager.java
 * 
 * Copyright (c) 2006 - 2007
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.xfc.util;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.SwingUtilities;

import org.xfc.XApp;
import org.xfc.components.XIconButton;
import org.xfc.components.XStatusBar;
import org.xfc.dialog.XErrorDialog;
import org.xfc.eventbus.XProgressEvent;
import org.xfc.eventbus.XStatusEvent;

// TODO: bulk this implementation up, add progress dialogs and cancellablilty

// TODO: use XProgressDialog

// TODO: always show the task as an icon in the status bar.
//       if a task is model, then also display it in a dialog
//       if not, wait 1-2 seconds. if the task hasn't finished, then display it in a dialog.

// TODO: one task manager per application? Should it be a service?


/**
 * 
 * 
 * @author Devon Carew
 */
public class XTaskManager
{
	private static XTaskManager taskManager = new XTaskManager();
	
	private XIconButton	taskButton;
	
	private List 		tasks = Collections.synchronizedList(new ArrayList());
	
	private boolean		processingTask;
	
	private XTaskManager()
	{
		// TODO: I believe this icon / label needs to be animating a bit, to draw the
		// user's eye to it.
		taskButton = new XIconButton(XTask.getDefaultTaskIcon());
		taskButton.setVisible(false);
		taskButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				showTasksDialog();
			}
		});
		
		// TODO: should we assume that all apps have a status bar?
		XStatusBar statusBar = XApp.getApp().getActiveAppFrame().getStatusBar();
		statusBar.addComponent(taskButton);
		
		new Thread("XTaskManager queue") {
			{ setDaemon(true); }
			
			public void run() {
				serviceQueue();
			}
		}.start();
	}

	public static XTaskManager getTaskManager()
	{
		return taskManager;
	}
	
	private void showTasksDialog()
	{
		// The user hit the task button.
		
		System.out.println("beep beep");
	}
	
	public void scheduleTask(XTask task)
	{
		synchronized (tasks)
		{
			tasks.add(task);
			
			tasks.notify();
		}
	}
	
	/**
	 * @return Returns true if this task manager is processing a task, or there are tasks in the queue.
	 */
	public boolean isProcessingTasks()
	{
		return processingTask || !tasks.isEmpty();
	}
	
	private void serviceQueue()
	{
		while (true)
		{
			XTask task = null;
			
			synchronized (tasks)
			{
				if (!tasks.isEmpty())
				{
					task = (XTask)tasks.remove(0);
					
					processingTask = true;
				}
				else
				{
					try
					{
						tasks.wait();
					}
					catch (InterruptedException ie)
					{
						return;
					}
				}
			}
			
			if (task != null)
			{
				try
				{
					handleTask(task);
				}
				catch (Throwable t)
				{
					if (t instanceof InterruptedException)
						return;
					
					XErrorDialog dialog = new XErrorDialog(
						"Task Manager Exception", "Exception while executing task '" + task.getTaskName() + "'.", t);
					
					dialog.setSubMessage(t.toString());
					
					dialog.show();
				}
				finally
				{
					processingTask = false;
				}
			}
		}
	}
	
	private void handleTask(final XTask task)
	{
		// TODO: use FoxTrot to clean this code up.
		
		invokeAndWait(new Runnable() {
			public void run() {
				task.taskWasStarted();
				
				if (!task.taskPreFlight())
				{
					task.taskWasStopped();
					
					return;
				}
				
				XApp.getApp().getEventBus().publish(new XStatusEvent(task, task.getTaskName()));
				XApp.getApp().getEventBus().publish(new XProgressEvent(task, XProgressEvent.INDETERMINATE_START));
				
				taskButton.setToolTipText(task.getTaskName());
				taskButton.setIcon(task.getIcon());
				taskButton.setVisible(true);
			}
		});
		
		final Object[]		result = new Object[1];
		final Throwable[]	throwable = new Throwable[1];
		
		try
		{
			result[0] = task.performWork();
		}
		catch (Throwable t)
		{
			throwable[0] = t;
		}
		
		invokeAndWait(new Runnable() {
			public void run() {
				task.taskWasStopped();
				taskButton.setVisible(false);
				
				XApp.getApp().getEventBus().publish(new XStatusEvent(task, null));
				XApp.getApp().getEventBus().publish(new XProgressEvent(task, XProgressEvent.INDETERMINATE_END));
				
				if (throwable[0] != null)
				{
					task.taskPostFlightException(throwable[0]);
				}
				else
				{
					task.taskPostFlightSuccess(result[0]);
				}
			}
		});
	}
	
	private static void invokeAndWait(Runnable runnable)
	{
		try
		{
			SwingUtilities.invokeAndWait(runnable);
		}
		catch (InterruptedException e)
		{
			throw new RuntimeException(e);
		}
		catch (InvocationTargetException e)
		{
			throw new RuntimeException(e);
		}
	}
	
}
